package Util.HttpClientUtil;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

import javax.xml.bind.annotation.XmlElementDecl.GLOBAL;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class MyRSASign {
	public static final int PRIVATE = 0;
	public static final int PUBLIC = 1;
	private static Log logger = LogFactory.getLog(HttpClientUtil.class);
	private static String filePath = GLOBAL.class.getResource("").getFile() + "..";
	public static final String rootPath = filePath.substring(0, filePath.indexOf("WEB-INF") + 8);
	public static final String path = rootPath + "key" + File.separator;

	/**
	 * 加签 value 需加签字段， privateKeyName 秘钥文件名 返回：加签之后的串
	 */
	public static String addSign(String vale, String privateKeyName) {
		String keypath = path + privateKeyName;
		// 签名示例
		logger.info("开始签名...");
		// 从文件中读取私钥
		String signatureInBase64 = "";
		try {
			PrivateKey privateKeyLoadedFromFile = (PrivateKey) getKeyFromFile(keypath, PRIVATE);
			// 初始化签名算法
			Signature sign = Signature.getInstance("SHA256withRSA");
			// 建议SHA256withRSA
			// sign.initSign(privateKey);//指定签名所用私钥
			sign.initSign(privateKeyLoadedFromFile);
			// 指定使用从文件中读取的私钥
			byte[] data = vale.getBytes();// 待签名明文数据
			logger.info("待签名的明文串: " + new String(data));
			// 更新用于签名的数据
			sign.update(data);

			// 签名
			byte[] signature = sign.sign();
			// 将签名signature转为BASE64编码，用于HTTP传输
			Base64 base64 = new Base64();
			signatureInBase64 = base64.encodeToString(signature);
			logger.info("Base64格式编码的签名串: " + signatureInBase64);
			logger.info("签名结束...");
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SignatureException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return signatureInBase64;
	}

	/**
	 * 验签 value 待验签明文数据， signature 收到的签名（报文中的签名） publickeyName 公钥文件名
	 * 
	 */
	public static boolean varifySign(String publickeyName, String value, String signature) {
		String keypath = path + publickeyName;
		boolean flag = false;
		// 验签示例
		logger.info("验签开始...");
		// 从文件中读取公钥
		try {
			PublicKey publicKeyLoadedFromFile = (PublicKey) getKeyFromFile(keypath, PUBLIC);
			// 将签名signature转为BASE64编码，用于HTTP传输
			Base64 base64 = new Base64();
			// 从Base64还原得到签名
			byte[] signatureFromBase64 = base64.decodeBase64(signature);
			// 初始化验签算法
			Signature verifySign = Signature.getInstance("SHA256withRSA");
			// verifySign.initVerify(publicKey);
			// 指定验签所用公钥
			verifySign.initVerify(publicKeyLoadedFromFile);
			byte[] data = value.getBytes();// 待签名明文数据
			// data为待验签的数据(明文)
			verifySign.update(data);
			logger.info("待验签的明文串：" + new String(data) + " 签名（BASE64格式）：" + signature);
			// 验签
			flag = verifySign.verify(signatureFromBase64);
			logger.info("验签是否通过:" + flag);// true为验签成功
			logger.info("验签结束!...");
		} catch (FileNotFoundException e) {
			logger.info("验签异常!..." + e);
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			logger.info("验签异常!..." + e);
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			logger.info("验签异常!..." + e);
			e.printStackTrace();
		} catch (SignatureException e) {
			// TODO Auto-generated catch block
			logger.info("验签异常!..." + e);
			e.printStackTrace();
		}
		return flag;
	}

	private static Key getKeyFromFile(String filename, int type) throws FileNotFoundException {
		FileInputStream fis = null;
		fis = new FileInputStream(filename);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		int b;
		try {
			while ((b = fis.read()) != -1) {
				baos.write(b);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		byte[] keydata = baos.toByteArray();

		Key key = null;
		try {
			KeyFactory kf = KeyFactory.getInstance("RSA");
			switch (type) {
			case PRIVATE:
				PKCS8EncodedKeySpec encodedPrivateKey = new PKCS8EncodedKeySpec(keydata);
				key = kf.generatePrivate(encodedPrivateKey);
				return key;
			case PUBLIC:
				X509EncodedKeySpec encodedPublicKey = new X509EncodedKeySpec(keydata);
				key = kf.generatePublic(encodedPublicKey);
				return key;
			}
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeySpecException e) {
			e.printStackTrace();
		}

		return key;
	}

	public static void main(String[] args) throws IOException {
		try {

			// 初始化
			KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");

			// 生成RSA公私钥对
			// log.info("生成公私钥对...");
			// int KEY_LENGTH=1024;//密钥长度，默认1024
			// gen.initialize(KEY_LENGTH);
			// log.info("\tRSA密钥长度："+KEY_LENGTH);
			// KeyPair pair = gen.generateKeyPair();
			// PublicKey publicKey = pair.getPublic();
			// PrivateKey privateKey = pair.getPrivate();
			// log.info("\t公钥格式: "+publicKey.getFormat()+", 私钥格式:
			// "+privateKey.getFormat());
			//// log.info("publicKey: " + new String(publicKey.getEncoded()));
			//// log.info("privateKey: " + new String(privateKey.getEncoded()));
			//
			// //将私钥按默认格式编码写入到普通文件中
			// log.info("\t公钥写入PublicKey.dat文件，私钥写入PrivateKey.dat文件");
			// FileOutputStream privateKeyFile = new
			// FileOutputStream(path+"PrivateKey.der");
			// privateKeyFile.write(privateKey.getEncoded());
			// //对私钥调用getEncoded()获得的是PKCS#8格式
			// privateKeyFile.close();
			// //将公钥按默认格式编码写入到普通文件中
			// FileOutputStream publicKeyFile = new
			// FileOutputStream(path+"PublicKey.der");
			// publicKeyFile.write(publicKey.getEncoded());
			// //对公钥调用getEncoded()获得的是X.509格式
			// publicKeyFile.close();
			// log.info("密钥生成完毕！\r\n");

			// 签名示例
			logger.info("签名示例...");
			// 从文件中读取私钥
			PrivateKey privateKeyLoadedFromFile = (PrivateKey) getKeyFromFile(path + "MyPrivateKey.dat", PRIVATE);
			// log.info("\t从文件中读取私钥...\r\n\t验证：从文件中读取的私钥是否与此前生成的私钥一致："+privateKeyLoadedFromFile.equals(privateKey));

			// 初始化签名算法
			Signature sign = Signature.getInstance("SHA256withRSA");// 建议SHA256withRSA
			logger.info("\t签名算法: " + sign.getAlgorithm());
			// sign.initSign(privateKey);//指定签名所用私钥
			sign.initSign(privateKeyLoadedFromFile);// 指定使用从文件中读取的私钥

			byte[] data = "1492160479972||20170220|1|01|201704120001|20170413|1000|100|10".getBytes();// 待签名明文数据
			logger.info("\t待签名的明文串: " + new String(data));
			// 更新用于签名的数据
			sign.update(data);
			// 签名
			byte[] signature = sign.sign();
			// 将签名signature转为BASE64编码，用于HTTP传输
			Base64 base64 = new Base64();
			String signatureInBase64 = base64.encodeToString(signature);
			logger.info("\tBase64格式编码的签名串: " + signatureInBase64);
			logger.info("签名示例结束！\r\n");

			// 验签示例
			logger.info("验签示例...");
			// 从文件中读取公钥
			PublicKey publicKeyLoadedFromFile = (PublicKey) getKeyFromFile(path + "JCTPublicKey.dat", PUBLIC);
			// log.info("\t从文件中读取公钥...\r\n\t\t验证：从文件中读取的公钥是否与此前生成的公钥一致：："+publicKeyLoadedFromFile.equals(publicKey));

			// 从Base64还原得到签名
			byte[] signatureFromBase64 = base64.decodeBase64(signatureInBase64);
			logger.info(
					"\t从BASE64还原签名...\r\n\t\t验证：还原得到的签名是否与此前生成的签名一致：：" + Arrays.equals(signatureFromBase64, signature));

			// 初始化验签算法
			Signature verifySign = Signature.getInstance("SHA256withRSA");
			// verifySign.initVerify(publicKey);
			// 指定验签所用公钥
			verifySign.initVerify(publicKeyLoadedFromFile);

			// data为待验签的数据(明文)
			verifySign.update(data);
			logger.info("\t待验签的明文串：" + new String(data) + " 签名（BASE64格式）：" + signatureInBase64);
			// 验签
			boolean flag = verifySign.verify(signatureFromBase64);
			logger.info("\t验签是否通过:" + flag);// true为验签成功
			logger.info("验签示例结束！");

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}
